﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

using Eflong.Structure;
using Eflong.Utility;

namespace Eflong.SocketClient
{
    public enum Recv { RecvNone = 0 , RecvId = 1 , RecvLength = 2 , RecvData = 3 };

    public delegate void SocketConnectedHandler( object sender , EventArgs e );

    public delegate void SocketDisConnectHandler( object sender , EventArgs e );

    public class ClientSocket : IDisposable
    {
        private const int m_nRecvDataLen = 1024 * 100;// 1640 * 10; 1024*8;
        private const int nBufSize = m_nRecvDataLen * 2; //
        private const int nRecvSize = 4 * nBufSize;
        private const int timeOut = 60 * 1000;

        /// <summary>
        /// Socket 对象
        /// </summary>
        private Socket clientSocket;

        /// <summary>
        /// 接受数据包的临时接受区
        /// </summary>
        private byte[ ] m_lpbyRecvBuf = new byte[ nBufSize ];

        /// <summary>
        /// 当前接受的数据包的缓冲区
        /// </summary>
        private byte[ ] recvBuf = new byte[ nRecvSize ];     // 实际要求  id = 5; dwSize= 36224;

        /// <summary>
        /// 当前的包开始位置
        /// </summary>
        private int recvPos = 0;

        /// <summary>
        /// 当前发送数据包的临时缓冲区
        /// </summary>
        private byte[ ] m_lpbySendBuf;

        /// <summary>
        /// 服务器的IP
        /// </summary>
        public string ServerHost { get; set; }

        /// <summary>
        /// 服务器的端口
        /// </summary>
        public int Port { get; set; }

        public Recv recvState = Recv.RecvId;

        public Client client;

        /// <summary>
        /// Listener endpoint.
        /// </summary>
        private IPEndPoint hostEndPoint;

        /// <summary>
        /// Signals a connection.
        /// </summary>
        private static AutoResetEvent autoConnectEvent = new AutoResetEvent( false );

        /// <summary>
        /// Constants for socket operations.
        /// </summary>
        private const Int32 ReceiveOperation = 1 , SendOperation = 0;

        /// <summary>
        /// Signals the send/receive operation.
        /// </summary>
        private static AutoResetEvent[ ] autoSendReceiveEvents = new AutoResetEvent[ ]
        {
            new AutoResetEvent(false),
            new AutoResetEvent(false)
        };

        /// <summary>
        /// 连接完成
        /// </summary>
        public event SocketConnectedHandler onCompleted;

        /// <summary>
        /// 接受到数据时的委托
        /// </summary>
        /// <param name="info"></param>
        public delegate void ReceivePacketHandler( Packet packet );

        /// <summary>
        /// 开始监听数据的委托
        /// </summary>
        public delegate void StartListenHandler( );

        /// <summary>
        /// 发送信息完成的委托
        /// </summary>
        /// <param name="successorfalse"></param>
        public delegate void SendCompleted( bool successorfalse );

        /// <summary>
        /// 发送信息完成的事件
        /// </summary>
        public event SendCompleted OnSended;

        public ClientSocket( Client client )
        {
            this.client = client;
        }

        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool ConnectToSocketServer( string ip , int port )
        {
            ServerHost = ip;
            Port = port;
            try
            {
                this.hostEndPoint = new IPEndPoint( IPAddress.Parse( ServerHost ) , Port );
            }
            catch ( Exception ex )
            {
                client.DisptchExceptionMessage( ex.Message );
                this.hostEndPoint = null;
                return false;
            }

            Connect( );

            return IsConnected( );
        }

        /// <summary>
        /// 建立socket连接
        /// </summary>
        public void Connect( )
        {
            if ( IsConnected( ) ) return;

            try
            {
                if ( hostEndPoint == null )
                {
                    this.hostEndPoint = new IPEndPoint( IPAddress.Parse( ServerHost ) , Port );
                }

                if ( clientSocket != null )
                {
                    this.clientSocket.Close( );

                    this.clientSocket = null;
                }

                this.clientSocket = new Socket( this.hostEndPoint.AddressFamily , SocketType.Stream , ProtocolType.Tcp );

                SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs( ) { UserToken = this.clientSocket , RemoteEndPoint = this.hostEndPoint };

                connectArgs.Completed += this.SocketAsyncEventArgs_OnConnectCompleted;

                clientSocket.ConnectAsync( connectArgs );
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "ClientSocket Connect: " + ex.Message );
            }
        }

        /// <summary>
        /// 新的建立连接的事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SocketAsyncEventArgs_OnConnectCompleted( object sender , SocketAsyncEventArgs e )
        {
            try
            {
                if ( IsConnected( ) )
                {
                    var response = new byte[ 1024 ];
                    e.SetBuffer( response , 0 , response.Length );

                    e.Completed -= this.SocketAsyncEventArgs_OnConnectCompleted;
                    e.Completed += this.SocketAsyncEventArgs_OnReceiveOrOnSend;

                    this.clientSocket.ReceiveAsync( e );
                }

                if ( this.onCompleted != null )
                {
                    onCompleted( this , EventArgs.Empty );
                }
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "SocketAsyncEventArgs_OnConnectCompleted :" + ex.Message );
                return;
            }
        }

        /// <summary>
        /// 接受数据结束事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SocketAsyncEventArgs_OnReceiveOrOnSend( object sender , SocketAsyncEventArgs e )
        {
            switch ( e.LastOperation )
            {
                case SocketAsyncOperation.Receive:

                    OnReceive( sender , e );
                    break;

                case SocketAsyncOperation.Send:
                    OnSend( sender , e );
                    break;
            }
        }

        private void OnReceive( object sender , SocketAsyncEventArgs e )
        {
            try
            {
                LogWrapper.Logger.Info( "接受数据事件 -- 接收字节： " + e.BytesTransferred.ToString( ) );
                autoSendReceiveEvents[ SendOperation ].Set( );

                ProcessReceive( e );
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "OnReceive" + ex.Message );
            }
        }

        /// <summary>
        /// 处理接受到的数据
        /// </summary>
        /// <param name="e"></param>
        private void ProcessReceive( SocketAsyncEventArgs e )
        {
            if ( e.LastOperation != SocketAsyncOperation.Receive )
            {
                return;
            }
            try
            {
                if ( e.BytesTransferred > 0 && e.SocketError == SocketError.Success )
                {
                    if ( e.SocketError == SocketError.Success )
                    {
                        Int32 byteTransferred = recvPos + e.BytesTransferred;

                        if ( e.Offset > 0 )
                        {
                        }
                        Int32 dwSize = ReceivePacket( e );
                        if ( bRecvError )
                        {
                            nRecvBufPos = 0;
                            if ( nRecvError >= 5 )
                            {
                                nRecvError = 0;

                                // 认为接收数据出现问题了，关闭当前的 socket
                                this.client.DisptchExceptionMessage( "数据包字节太大了，网络可能有错误" );
                                this.CloseClientSocket( e , true );

                                return;
                            }
                            else
                            {
                                // 放弃当前的接受数据
                                LogWrapper.Logger.Warn( "放弃当前的接受数据 , 次数：" + nRecvError.ToString( ) );
                            }
                        }

                        //在这里循环监听

                        Boolean willRaiseEvent = ( e.UserToken as Socket ).ReceiveAsync( e );
                        if ( !willRaiseEvent )
                        {
                            LogWrapper.Logger.Warn( "发生循环监听" );
                            ProcessReceive( e );
                        }
                    }
                    else
                    {
                        this.client.DisptchExceptionMessage( "接受数据有错误" );
                    }
                }
                else
                {
                    this.CloseClientSocket( e , true );
                }
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "ProcessReceive" + ex.Message );
            }
        }

        /// <summary>
        /// 当前的接受错误次数

        /// </summary>
        private int nRecvError = 0;

        private bool bRecvError = false;
        private uint id;
        private int dwSize;
        private int nRecvBufPos = 0;
        private bool bCompleted = true;

        private int ReceivePacket( SocketAsyncEventArgs e )
        {
            int length;
            try
            {
                bRecvError = false;

                string s = string.Format( "ReceivePacket BytesTransferred = {0}, Offset = {1} , recvPos = {2} " , e.BytesTransferred , e.Offset , recvPos );
                LogWrapper.Logger.Debug( s );
                Array.Copy( e.Buffer , e.Offset , recvBuf , nRecvBufPos , e.BytesTransferred );

                length = nRecvBufPos + e.BytesTransferred;
                do
                {
                    // 应考虑一次出现多个包，或包不完整的情况

                    if ( bCompleted )
                    {
                        id = Common.GetUIntFromByteArray( recvBuf , ref recvPos );
                        dwSize = ( int )Common.GetUIntFromByteArray( recvBuf , ref recvPos );
                    }

                    if ( dwSize > nRecvSize )// 1024*50 )
                    {
                        bCompleted = true;

                        // 数据包字节太大了，可能有错误
                        string s1 = string.Format( "接受的数据包字节太大了，不正确！id = {0}; dwSize= {1}; recvPos = {2} length = {3} " , id , dwSize , recvPos , length );
                        LogWrapper.Logger.Error( s1 );
                        nRecvError++;

                        // 放弃接收的当前数据
                        recvPos = 0;
                        nRecvBufPos = 0;
                        bRecvError = true;

                        return length + ( int )dwSize;
                    }
                    if ( length >= recvPos + dwSize )
                    {
                        nRecvError = 0;
                        bCompleted = true;
                        byte[ ] baData = new byte[ dwSize ];
                        Array.Copy( recvBuf , recvPos , baData , 0 , ( int )dwSize );
                        recvPos += ( int )dwSize;
                        string s2 = string.Format( "接受的数据包 id = {0}; dwSize= {1}; recvPos = {2} length = {3} " , id , dwSize , recvPos , length );
                        LogWrapper.Logger.Debug( s2 );
                        Packet packet = new Packet( dwSize , baData );
                        packet.m_dwId = id;

                        if ( id == 0 )
                        {
                            client.dispatcher.DispatchPacketReceived( packet );
                        }
                        else
                        {
                            client.clientThread.AddPacketReceived( packet );
                        }
                    }
                    else
                    {
                        bCompleted = false;
                        int len = recvPos + dwSize - length;
                        string s3 = string.Format( "接受的数据包不完整！id = {0}; dwSize= {1}; recvPos = {2} length = {3}  缺字节数： recvPos + dwSize - length = {4} " , id , dwSize , recvPos , length , len );
                        LogWrapper.Logger.Debug( s3 );
                        int nbytes = length - recvPos;
                        if ( recvPos == 8 )
                        {
                            nRecvBufPos = length;
                        }
                        else if ( recvPos > 8 )
                        {
                            recvPos -= 8;
                            nbytes = length - recvPos;
                            byte[ ] baData = new byte[ nbytes ];
                            Array.Copy( recvBuf , recvPos , baData , 0 , nbytes );
                            Array.Copy( baData , 0 , recvBuf , 0 , nbytes );
                            baData = null;
                            nRecvBufPos = nbytes;
                            recvPos = 8;
                        }
                        else
                        {
                            LogWrapper.Logger.Warn( "放弃不正确的包" );
                            bCompleted = true;
                            break;
                        }
                        break;
                    }
                } while ( recvPos < length );
                if ( bCompleted )
                {
                    // 重置缓冲区的开始位置
                    recvPos = 0;
                    nRecvBufPos = 0;
                }
                return length;
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "ReceivePacket" );
                LogWrapper.Logger.Error( ex );
                return nRecvSize * 10;
            }
        }

        /// <summary>
        /// 关闭 socket
        /// </summary>
        public void CloseClientSocket( SocketAsyncEventArgs e , bool bRecvCompleted )
        {
            try
            {
                SocketAsyncEventArgs_Dispose( e );

                CloseClientSocket( );
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "CloseClientSocket : " + ex.Message );
            }
        }

        private void SocketAsyncEventArgs_Dispose( SocketAsyncEventArgs e )
        {
            e.Completed -= SocketAsyncEventArgs_OnReceiveOrOnSend;
            e.Dispose( );
        }

        private void CloseClientSocket( )
        {
            recvPos = 0;
            recvState = Recv.RecvNone;
            if ( clientSocket != null )
            {
                try
                {
                    clientSocket.Close( );   // 直接关闭就可以了
                    clientSocket = null;
                }
                catch ( Exception ex )
                {
                    LogWrapper.Logger.Error( "CloseClientSocket : " + ex.Message );
                }

                if ( this.onCompleted != null )
                {
                    this.onCompleted( this , EventArgs.Empty );
                }
            }
        }

        /// <summary>
        /// Disconnect 终止连接
        /// </summary>
        public void Disconnect( )
        {
            try
            {
                if ( clientSocket != null && clientSocket.Connected )
                {
                    LogWrapper.Logger.Info( "Disconnect 终止连接" );
                }
                clientSocket.Close( );
                clientSocket = null;
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "Disconnect() err:" + ex.Message );
            }
        }

        public bool IsConnected( )
        {
            if ( clientSocket != null )
                return clientSocket.Connected;
            else
                return false;
        }

        /// <summary>
        /// 加入一个包
        /// </summary>
        /// <param name="status"></param>
        private void AddPacket( object state )
        {
            Packet packet = ( Packet )state;

            client.clientThread.AddPacketReceived( packet );
        }

        /// <summary>
        /// 异步发送数据包
        /// </summary>
        /// <param name="pPacket"></param>
        public void AsyncSend( Packet pPacket )
        {
            if ( !IsConnected( ) ) return;

            if ( m_lpbySendBuf != null ) m_lpbySendBuf = null;
            uint dwDataSize = pPacket.GetDataSize( );

            byte[ ] bsId = Eflong.MonitoringLib.Common.KWConvert.GetByteArrayFromUInt( pPacket.m_dwId );
            byte[ ] bsSize = Eflong.MonitoringLib.Common.KWConvert.GetByteArrayFromUInt( dwDataSize );

            m_lpbySendBuf = new byte[ ( int )dwDataSize + 8 ];

            Array.Copy( bsId , 0 , m_lpbySendBuf , 0 , 4 );
            Array.Copy( bsSize , 0 , m_lpbySendBuf , 4 , 4 );
            Array.Copy( pPacket.GetpData( ) , 0 , m_lpbySendBuf , 8 , dwDataSize );
            SendData( m_lpbySendBuf );
        }

        /// <summary>
        /// 异步发送数据包的字节串
        /// </summary>
        /// <param name="buf"></param>
        public void SendData( byte[ ] sendBuffer )
        {
            SocketAsyncEventArgs completeArgs = new SocketAsyncEventArgs( );
            completeArgs.SetBuffer( sendBuffer , 0 , sendBuffer.Length );
            completeArgs.UserToken = this.clientSocket;

            completeArgs.Completed += this.SocketAsyncEventArgs_OnReceiveOrOnSend;

            try
            {
                clientSocket.SendAsync( completeArgs );
            }
            catch ( Exception ex )
            {
                this.CloseClientSocket( completeArgs , false );
                LogWrapper.Logger.Error( ex.Message );
            }
        }

        private void OnSend( object sender , SocketAsyncEventArgs e )
        {
            try
            {
                if ( OnSended != null )
                {
                    if ( e.SocketError == SocketError.Success )
                    {
                        OnSended( true );
                    }
                    else
                    {
                        OnSended( false );

                        CloseClientSocket( e , false );
                        return;
                    }
                }
                SocketAsyncEventArgs_Dispose( e );
            }
            catch ( Exception ex )
            {
                LogWrapper.Logger.Error( "OnSend : " + ex.Message );
            }
        }

        /// <summary>
        /// Exchange a message with the host.
        /// </summary>
        /// <param name="message">Message to send.</param>
        /// <returns>Message sent by the host.</returns>
        internal String SendReceive( String message )
        {
            if ( this.IsConnected( ) )
            {
                // Create a buffer to send.
                Byte[ ] sendBuffer = Encoding.ASCII.GetBytes( message );

                // Prepare arguments for send/receive operation.
                SocketAsyncEventArgs completeArgs = new SocketAsyncEventArgs( );
                completeArgs.SetBuffer( sendBuffer , 0 , sendBuffer.Length );
                completeArgs.UserToken = this.clientSocket;
                completeArgs.RemoteEndPoint = this.hostEndPoint;
                completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>( OnSend );

                // Start sending asyncronally.
                clientSocket.SendAsync( completeArgs );

                // Wait for the send/receive completed.
                AutoResetEvent.WaitAll( autoSendReceiveEvents );

                // Return data from SocketAsyncEventArgs buffer.
                return Encoding.ASCII.GetString( completeArgs.Buffer , completeArgs.Offset , completeArgs.BytesTransferred );
            }
            else
            {
                throw new SocketException( ( Int32 )SocketError.NotConnected );
            }
        }

        /// <summary>
        /// Close socket in case of failure and throws a SockeException according to the SocketError.
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the failed operation.</param>
        private void ProcessError( SocketAsyncEventArgs e )
        {
            Socket s = e.UserToken as Socket;
            if ( s.Connected )
            {
                // close the socket associated with the client
                try
                {
                    s.Shutdown( SocketShutdown.Both );
                }
                catch ( Exception )
                {
                    // throws if client process has already closed
                }
                finally
                {
                    if ( s.Connected )
                    {
                        s.Close( );
                    }
                    e.Dispose( );
                }
            }
            client.DisptchExceptionMessage( "SendData err: " + e.SocketError.ToString( ) );
        }

        #region IDisposable Members

        /// <summary>
        /// Disposes the instance of SocketClient.
        /// </summary>
        public void Dispose( )
        {
            autoConnectEvent.Close( );
            autoSendReceiveEvents[ SendOperation ].Close( );
            autoSendReceiveEvents[ ReceiveOperation ].Close( );
            if ( this.clientSocket.Connected )
            {
                this.clientSocket.Close( );
            }
        }

        #endregion IDisposable Members
    }
}